<?php

/**
 * QPHP
 *
 * Copyright (c) 2022 QPHP
 *
 * @author lbnnbs
 * @copyright Copyright (c) 2022 lbnnbs QPHP (https://gitee.com/lbnnbs/qphp)
 * @license AGPLv3 License
 * 
 * HMVC 插件
 * 
 * @example 
 * 在视图中调用一个公共的 header 内容
 * 
 * <?php echo Hmvc::load('public','header')?> // 相当于执行了当前模块内Public控制器的header动作
 */
class Hmvc {

    /**
     * 防止类实例化或被复制
     *
     */
    private function __construct() {
        
    }

    private function __clone() {
        
    }

    /**
     * 运行指定的控制器内的动作并返回输出的结果
     *
     * @param string $controllerName 控制器名称，区分大小写
     * @param string $actionName  动作名, 默认为 'index'，区分大小写
     * @param array $params 参数数组，如：array('id'=>1,'name'=>'lbnnbs')
     * 
     * @example 
     * 在视图中调用一个公共的 header 内容
     * <?php echo Hmvc::load('public','header')?> // 相当于执行了当前模块内Public控制器的header动作
     */
    static public function load($controllerName, $actionName = Controller::DEFAULT_ACTION, $params = array()) {

        // 克隆一个请求对象，防止和调用Hmvc的控制器的请求参数相互干扰
        $request = clone Request::getInstance();

        // 注入http参数，供控制器中使用
        if ($params) {
            $request->setParam($params)->setGet($params);
        }

        // 得到最终控制器和动作
        $controller = self::_getController($controllerName, $request);

        // 判断动作是否存在
        if (!method_exists($controller, $actionName)) {
            throw new Exception('控制器：' . get_class($controller) . ' 中未定义动作：' . htmlspecialchars($actionName, ENT_QUOTES));
        }

        $request->setController($controllerName);
        $request->setAction($actionName);

        // 把执行的输出先保存起来
        ob_start();
        // 执行 init 方法
        $controller->init();
        // 执行动作
        $controller->$actionName();
        // 得到所有输出内容
        $viewContent = ob_get_clean();

        // 是否自动输出视图
        if ($controller->viewIsAutoRender()) {
            $viewContent .= $controller->render($actionName);
        }

        return $viewContent;
    }

    /**
     * 根据控制器名得到控制器对象
     *
     * @param string $controllerName
     * @return object
     */
    static private function _getController($controllerName, $request) {

        // 得到控制器文件名
        $controllerFile = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, APP_PATH . '/' . MODULE_NAME . '/Controller/' . $controllerName . '.php');

        if (!file_exists($controllerFile)) {
            throw new Exception("控制器不存在：{$controllerFile}");
        }

        // 包含控制器生成对象
        require_once $controllerFile;

        // 得到控制器类完整名称
        $className = str_replace('/', '\\', APP_DIR . '\\' . MODULE_NAME . '\\Controller\\' . $controllerName);

        // 类是否存在的
        if (!class_exists($className, false)) {
            throw new Exception("类：{$className} 未定义在：{$controllerFile}");
        }

        $controller = new $className($request);

        // 判断控制器是否继承基类
        if (!($controller instanceof Controller)) {
            throw new Exception("控制器类 {$className} 必需继承 Controller 基类");
        }

        return $controller;
    }

}
